package model.towers;

import java.util.concurrent.CopyOnWriteArrayList;

import model.bullets.IBullet;
import model.enemies.IEnemy;
import model.surface.Scale;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;

/**
 * Abstract class implements all tower methods for the game.
 * 
 * @author EvilTeam
 * 
 */
public abstract class TowerSprite implements ITower {

	private static final int FIXING_BUG_WITH_DISPLAING_IMAGE = 4;
	private static final int NUMBER_OF_COLUMNS_IN_SPRITESHEET = 4;
	private static final int NUMBER_OF_ROWS_IN_SPRITESHEET = 2;
	private static final int OFFSET_FOR_INNER_CIRCLE = 10;
	private static final String INNER_CIRCLE = "#330099CC";
	private static final String OUTER_RING = "#330066FF";
	private Bitmap scaledUpgradeIcon, scaledSellIcon = null;
	private Scale scale = Scale.getInstanceOfScaleClass();
	private CopyOnWriteArrayList<IBullet> bullets = new CopyOnWriteArrayList<IBullet>();
	private int currentFrame;
	private int currentDirection = 0;
	private Bitmap bitmapImage, scaledImage;
	private int shootingSpeed;
	private float xAxisPoint, yAxisPoint;
	private float range;
	private int damage, price;
	private int currentFrameHeight, currentFrameWidth;
	private boolean isTouched;
	private StartShootingThread towerThread;
	private CopyOnWriteArrayList<IEnemy> enemies;
	private IEnemy target = null;
	private int numberOfAllowedUpgrades = 0;
	private boolean isSold = false;

	/**
	 * Draws tower and calls method which draws bullet.
	 */
	@Override
	public void draw(Canvas canvas) {
		int xStartPoint = currentFrame * currentFrameWidth;
		int yStartPoint = currentDirection * currentFrameHeight;

		Rect sourceRectangular = new Rect(xStartPoint
				+ FIXING_BUG_WITH_DISPLAING_IMAGE, yStartPoint, xStartPoint
				+ this.getWidth(), yStartPoint + this.getHeight());

		Rect destinationRectangular = new Rect((int) xAxisPoint
				- this.getWidth() / 2, (int) yAxisPoint - this.getHeight() / 2,
				(int) xAxisPoint + this.getWidth() / 2, (int) yAxisPoint
						+ this.getHeight() / 2);
		canvas.drawBitmap(scaledImage, sourceRectangular,
				destinationRectangular, null);

		drawBullets(canvas);
	}

	/**
	 * Draws bullet and also checks if bullet has hit the enemy, the bullet is
	 * removed.
	 * 
	 * @param canvas
	 */
	private void drawBullets(Canvas canvas) {
		for (IBullet bullet : bullets) {
			if (!bullet.isHit() && !enemies.isEmpty()) {
				bullet.draw(canvas);
			} else {
				bullets.remove(bullet);
				bullet = null;
			}
		}
	}

	/**
	 * Draw range of the tower and draws icons for upgrade and sell in the range
	 * circle.
	 */
	@Override
	public void drawRange(Canvas canvas) {
		Paint rangePaint = new Paint();
		rangePaint.setStrokeWidth(10);
		rangePaint.setStyle(Paint.Style.STROKE);
		rangePaint.setColor(Color.parseColor(OUTER_RING));
		Paint circlePaint = new Paint();
		circlePaint.setColor(Color.parseColor(INNER_CIRCLE));
		canvas.drawCircle(this.getxAxisPoint(), this.getyAxisPoint(),
				this.getRange() - OFFSET_FOR_INNER_CIRCLE, circlePaint);
		canvas.drawCircle(this.getxAxisPoint(), this.getyAxisPoint(),
				this.getRange(), rangePaint);

		TowerSpriteSupplier.drawUpgradeIcon(canvas, this);
		TowerSpriteSupplier.drawSellIcon(canvas, this);
	}

	/**
	 * Checks if there is an enemy in the range of the current tower.
	 */
	@Override
	public boolean inRange(IEnemy target) {
		if (target != null)
			if (target.getX() > this.getxAxisPoint() - getRange()
					&& target.getX() < this.getxAxisPoint() + getRange()
					&& target.getY() > this.getyAxisPoint() - getRange()
					&& target.getY() < this.getyAxisPoint() + getRange()) {
				this.target = target;
				return true;
			}
		this.target = null;
		return false;
	}

	/**
	 * Updates view of the tower.
	 */
	@Override
	public void update() {
		if (target != null) {
			if (target.getSpeed().getyVelocity() != 0) {
				TowerSpriteSupplier.setTowerDirectionDependingOnX(target, this);
			} else {
				TowerSpriteSupplier.setTowerDirectionDependingOnY(target, this);
			}
		}
	}

	/**
	 * Checks if the tower is touched.
	 */
	@Override
	public boolean checkIfTheTowerIsTouched(float x, float y) {
		if (x >= (this.getxAxisPoint() - this.getWidth() / 2)
				&& x <= (this.getxAxisPoint() + this.getWidth() / 2)
				&& y >= (this.getyAxisPoint() - this.getHeight() / 2)
				&& y <= (this.getyAxisPoint() + this.getHeight() / 2)) {
			this.setTouched(true);
			return true;
		}
		this.setTouched(false);
		return false;
	}

	private void setCurrentFrameHeight() {
		this.currentFrameHeight = scaledImage.getHeight()
				/ NUMBER_OF_ROWS_IN_SPRITESHEET;
	}

	private void setCurrentFrameWidth() {
		this.currentFrameWidth = scaledImage.getWidth()
				/ NUMBER_OF_COLUMNS_IN_SPRITESHEET;
	}

	/**
	 * Set scaled bitmap image for the tower.
	 * 
	 * @param bitmapImage
	 */
	public void setBitmapImage(Bitmap bitmapImage) {
		if (bitmapImage != null) {
			this.bitmapImage = bitmapImage;
			scaledImage = Bitmap.createScaledBitmap(this.bitmapImage,
					(int) scale.getScaledWidthForImages(),
					(int) scale.getScaledHeightForImages(), true);
			setCurrentFrameHeight();
			setCurrentFrameWidth();
		}
	}

	public int getWidth() {
		return currentFrameWidth;
	}

	public int getHeight() {
		return currentFrameHeight;
	}

	@Override
	public void addBullet(IBullet bullet) {
		bullets.add(bullet);
	}

	@Override
	public float getxAxisPoint() {
		return xAxisPoint;
	}

	public void setxAxisPoint(float xAxisPoint) {
		if (xAxisPoint >= 0)
			this.xAxisPoint = xAxisPoint;
	}

	@Override
	public float getyAxisPoint() {
		return yAxisPoint;
	}

	public void setyAxisPoint(float yAxisPoint) {
		if (yAxisPoint >= 0)
			this.yAxisPoint = yAxisPoint;
	}

	@Override
	public int getPrice() {
		return price;
	}

	public void setPrice(int price) {
		if (price > 0)
			this.price = price;
	}

	@Override
	public int getShootingSpeed() {
		return shootingSpeed;
	}

	@Override
	public void setShootingSpeed(int shootingSpeed) {
		this.shootingSpeed = shootingSpeed;
	}

	@Override
	public float getRange() {
		return range;
	}

	@Override
	public void setRange(float range) {
		if (range > 0)
			this.range = range / scale.getScaleFactorForRange();
	}

	public boolean isTouched() {
		return isTouched;
	}

	public void setTouched(boolean isTouched) {
		this.isTouched = isTouched;
	}

	public CopyOnWriteArrayList<IBullet> getBullets() {
		return bullets;
	}

	public void setBullets(CopyOnWriteArrayList<IBullet> bullets) {
		this.bullets = bullets;
	}

	@Override
	public StartShootingThread getStartShootingThread() {
		return towerThread;
	}

	@Override
	public void setStartShootingThread(StartShootingThread towerThread) {
		this.towerThread = towerThread;
	}

	@Override
	public CopyOnWriteArrayList<IEnemy> getEnemy() {
		return enemies;
	}

	@Override
	public void setEnemy(CopyOnWriteArrayList<IEnemy> enemy) {
		this.enemies = enemy;
	}

	@Override
	public int getDamage() {
		return damage;
	}

	@Override
	public void setDamage(int damage) {
		this.damage = damage;
	}

	@Override
	public boolean isSold() {
		return isSold;
	}

	@Override
	public void setSold(boolean isSold) {
		this.isSold = isSold;
	}

	@Override
	public void setCurrentFrame(int currentFrame) {
		this.currentFrame = currentFrame;
	}

	@Override
	public void setCurrentDirection(int currentDirection) {
		this.currentDirection = currentDirection;
	}

	@Override
	public Bitmap getScaledUpgradeIcon() {
		return scaledUpgradeIcon;
	}

	@Override
	public void setScaledUpgradeIcon(Bitmap scaledUpgradeIcon) {
		this.scaledUpgradeIcon = scaledUpgradeIcon;
	}

	@Override
	public Bitmap getScaledSellIcon() {
		return scaledSellIcon;
	}

	@Override
	public void setScaledSellIcon(Bitmap scaledSellIcon) {
		this.scaledSellIcon = scaledSellIcon;
	}

	@Override
	public int getNumberOfAllowedUpgrades() {
		return numberOfAllowedUpgrades;
	}

	@Override
	public void setNumberOfAllowedUpgrades(int numberOfAllowedUpgrades) {
		this.numberOfAllowedUpgrades = numberOfAllowedUpgrades;
	}

	/**
	 * Compares the tower position and place the in right order on the game
	 * board.
	 */
	@Override
	public int compareTo(ITower another) {
		return (int) (this.getyAxisPoint() - another.getyAxisPoint());
	}
}